package disjunction.core.state
{
	import flash.utils.Dictionary;
	
	/**
	 * A state in a FSM, which may receive configuration data.
	 *
	 * @author Nick Wiggill
	 */
	
	public class StateManager
	{
		public const ERROR_CANNOT_CHANGE_TO_SAME_STATE:String = "Cannot change to same state.";
		public const ERROR_CANNOT_CHANGE_FROM_INACTIVE_STATE:String = "Cannot change from an inactive state.";
		public const ERROR_STATE_ID_ALREADY_EXISTS:String = "State name already exists.";
		public const ERROR_STATE_ID_UNDEFINED:String = "A valid State key string must be specified.";
		public const ERROR_STATE_UNDEFINED:String = "A valid State must be specified.";
		
		/** The hash of registered states. */
		protected var _states:Object = {};
		
		/** The active state. */
		protected var _activeState:State;

		/**
		 * Change to another registered state.
		 * 
		 * @param stateKey A string key representing the state to change to.
		 * @param data Any data to pass from the old state to the new. If
		 * multiple data objects need to be passed, they can be conglomerated
		 * as properties of a single object to pass as this parameter. This
		 * object also serves to tell what sort of transition this is, so that
		 * the state losing focus knows how to react.
		 */
		public function activateState(stateId:String, data:Object = null):void
		{
			if (!stateId)
				throw new Error(ERROR_STATE_ID_UNDEFINED);
			
			var state:State = _states[stateId];
			if (state == _activeState)
				throw new Error(ERROR_CANNOT_CHANGE_TO_SAME_STATE);

			_activeState = state;
			_activeState.activate(data);
			_activeState.addEventListener(StateChangeEvent.CHANGE, changeState);
		}
		
		public function deactivateState(stateKey:String):void
		{
			var state:State = _states[stateKey];
			if (state != _activeState)
				throw new Error(ERROR_CANNOT_CHANGE_FROM_INACTIVE_STATE);
			
			state.deactivate();
			state.removeEventListener(StateChangeEvent.CHANGE, changeState);
		}
		
		public function changeState(e:StateChangeEvent):void
		{
			deactivateState(e.fromStateId);
			activateState(e.toStateId, e.data);
		}
		
		/**
		 * Register a state for use.
		 * @param state The state instance to be registered.
		 * @param stateKey The name by which the state will be referenced.
		 */
		
		public function registerState(stateId:String, state:State):void
		{
			if (!state)
				throw new Error(ERROR_STATE_UNDEFINED);
			
			if (!stateId)
				throw new Error(ERROR_STATE_ID_UNDEFINED);
			
			if (_states.hasOwnProperty(stateId))
				throw new Error(ERROR_STATE_ID_ALREADY_EXISTS);
			
			_states[stateId] = state;
			//state.id = stateId;
		}
	}
}